import sys, os, re, random
from Tkinter import *
random.seed()

class SorterCallbackShim(object):
    """callback shim, handles kwd args"""
    def __init__(self, callback, *firstArgs):
        self.__callback = callback
        self.__firstArgs = firstArgs
    def __call__(self, *args):
        return self.__callback(*(self.__firstArgs + args))


                
class App(Frame):
    def __init__(self,master):      
        self.__appWidth = 800
        self.__appHeight = 600  
        self.__mainScreenWidth = 500
        self.__mainScreenHeight = 300
        
        Frame.__init__(self, master, borderwidth=2,background="red", height=self.__appHeight,width=self.__appWidth) #master is an instance of Tk()
        self.grid()
        
        self.__world = None
        self.__generation = 0
        
        #tells to increment generation, calc next state, and draw entities
        self.__go=False
        
        #write state history to output file, for replays and reverse
        self.__stateOutputFilePath = ''
        #predefined entity configuration, to replay
        self.configInputFilePath = ''
        #writes out entity config, for future replaying
        self.__configOutputFilePath = ''
        
        self.__rules = None
        self.__entityData = None
        self.__config = None
                     
        
        self.createWidgets()
        
    def createWidgets(self):
        #Resource file path, use resource file bool -
        #load previous configuration
        #(table of entity,type,xpos,ypos etc -__config
        #input Entites by Type - __entityData
        #input Rules __rules
        #canvas __canvas
        #GenerateButton calls __generate()
        #Play call _play(), Stop, Reverse, etc
        
        #Build GUI
        rowIndex = 0
        columnIndex = 0
        zeroColumnWidth = self.__mainScreenWidth
        oneColumnWidth = 300
        
        
        ##mainScreenArea Frame Widgets##
        mainScreenArea = LabelFrame(self,borderwidth=2,text="Generator Area",background="grey")
        mainScreenArea.grid(row=0,column=0)      
        #Instantiate main canvas
        self.__canvas = Canvas(mainScreenArea,bd=4, bg="#F8F8FF",height=self.__mainScreenHeight, width=self.__mainScreenWidth)
        self.__canvas.grid(row=0)
        
        
        ##OutputConsole Area
        outputConsoleArea = LabelFrame(self,borderwidth=2,text="Output Console",background="lightgrey")
        outputConsoleArea.grid(row=2,column=0)
        outputConsole = Canvas(outputConsoleArea,bd=4, bg="#6C7B8B",height=100, width=zeroColumnWidth)
        outputConsole.grid(row=2)
                
        ##Time Control Area
        controlArea = LabelFrame(self,text="Controls", bg="green")
        controlArea.grid(row=1,column=0,sticky="w")       
        
        reverseButtonCallBack = SorterCallbackShim(self.__reverse)
        self.reverseButton = Button(controlArea, text="Reverse", command=reverseButtonCallBack)
        self.reverseButton.grid(row=0,column=0)
        
        playButtonCallBack = SorterCallbackShim(self.__play)
        self.playButton = Button(controlArea, text="Play", command=playButtonCallBack)
        self.playButton.grid(row=0,column=1)
        
        stopButtonCallBack = SorterCallbackShim(self.__stop)
        self.stopButton = Button(controlArea, text="Stop",command=stopButtonCallBack)
        self.stopButton.grid(row=0,column=2)
        
        
        #Label Generation
        lblGeneration = Label(controlArea,text="Generation:",padx=20)
        lblGeneration.grid(row=0,column=3,columnspan=2)
        lblGenCounter = StringVar()
        lblGenerationCounter = Label(controlArea,textvariable=lblGenCounter)
        lblGenCounter.set("0")
        lblGenerationCounter.grid(row=0,column=5,columnspan=2)
        
        ##Right Section
        rightSectionArea = LabelFrame(self,borderwidth=1,text="RightSide",width=oneColumnWidth,background="white")
        rightSectionArea.grid(row=0,column=1,sticky="n",rowspan=3)
        
        ##Type Listbox
        strTypes = "Type1 Type2"
        varTypes = StringVar()
        varTypes.set(strTypes)
        lboxTypes = Listbox(rightSectionArea,listvariable=varTypes,height=4)
        lboxTypes.grid()
        
        ##World Controls
        worldControls = LabelFrame(rightSectionArea,text="World Controls",width=oneColumnWidth,background="darkgrey",borderwidth=1)
        worldControls.grid()
        
        generateWorldCallback = SorterCallbackShim(self.__generate)
        btnGenerateWorld = Button(worldControls,text="Generate",command=generateWorldCallback)
        btnGenerateWorld.grid(row=0,column=0)
        btnConfigureWorld = Button(worldControls,text="Configure")
        btnConfigureWorld.grid(row=0,column=1)
        
        
        ##Active Rules Listbox
        strActiveRules = "ActveRule1 ActiveRule2"
        varActiveRules = StringVar()
        varActiveRules.set(strActiveRules)
        lboxActiveRules = Listbox(rightSectionArea,listvariable=varActiveRules)
        lboxActiveRules.grid()
        
        ##Total Rules Listbox
        strTotalRules = "TotalRule1 TotalRule2"
        varTotalRules = StringVar()
        varTotalRules.set(strTotalRules)
        lboxTotalRules = Listbox(rightSectionArea,listvariable=varTotalRules)
        lboxTotalRules.grid()
        
    
    
    def __randomPosition(self):
        x = random.randrange(0,self.__mainScreenWidth)
        y = random.randrange(0,self.__mainScreenHeight)
        return [x,y]

    def __getRandomEntityPositionList(self,numberOfEntities):
        listEntities = []
        for i in range(0,numberOfEntities):
            listEntities.append(self.__randomPosition)
        return listEntities
   
    def __generate(self):                       
        #world instantiated with canvas it will draw on,
        #and rules it uses, config if one is used
        self._world = World(self.__canvas,self.__rules) #rules = the 'physics' of the world
        #instantiates the entites, if no config, gives random positions
        if not self.__config:
            a = self.__getRandomEntityPositionList(10)

        self.__world.createEntities(self.__entityData, self.__config)
        #draws instatiated entites in inital state
        self.__world.draw()  #calls draw on all entities
        
    def __play(self):
        self_go = True
        #open self__stateOutputFilePath and append to it (due to stops)
        stateFile = open(self._stateOutputFilePath, 'a')
        while self.__go:
            self.__generation +=1
            self._world.calcNextState() #world has all the info to calc next state
            #record state in file
            stateFile.write(self.__world.getState()+'\n')
            #draw new state to canvas
            self.__world.draw()
            
        stateFile.close()
        
    def __stop(self):
        self.__go = False
        
    def __reverse(self):
        self.__state #<--delete me
        #load self.__stateOutputFilePath, iterate over backwards
        
class World(object):
    def __init__(self, canvas, rules):
        self.__canvas = canvas
        self.__rules = rules
        self.__state = None #entType, xPos, yPos, size, direction, speed, etc.
        self.__entities = [] # list of all entity objects in world
        
    def getState(self):
        #return printable string describing current state
        self.__state #<--delete me
        
    def createEntities(self, entityData, config):
        if config:
            #load preconfigured entities and locations
            return
        #iterate thru entity data creating the entities
        
    def calcNextState(self):
        #apply rules to entities, update entity position attributes, save results
        #in self.__state
        newState = None
        for entity in self.__entities:
            sidesAdjoined = self.__getAdjoining(entity) # list of sides adjoined [L,U,D,R]
            #lookup rule that applies
            
    def draw(self):
        #draw all entities
        for entity in self.__entities:
            entity.draw()
            
    def __getAdjoining(self,entity):
        #scan self.__state to determine adjoining
        self.__state #<--delete me
        
#contiains generic attribs, methods of any entity
class SuperEntity(object):
    def __init__(self,canvas):
        self.__entityID = None
        self.__canvas = canvas
        self.__entityType = 'A'
        self.__color = (0,0,0)
        self.__xSize = 1
        self.__ySize = 1
        #motion
        self.__xPos = 0
        self.__yPos = 0
        self.__speed = 1
        #etc
        self.__trailLength = 0
        self.__trailFade = False
        
    #get/set
    def getColor(self):
        return self.__color
        
    def setColor(self,v):
        self.__color = v
    #etc
    
    #generic actions
    def move(self,direction): #or def getNextPosition(self,direction):
        #move in straight line in cardinal dirs
        if direction == 'U':
            self.__yPos -= self.__speed
        elif direction == 'R':
            self.__xPos += self.__speed
        elif direction == 'D':
            self.__yPos += self.__speed
        elif direction == 'L':
            self.__xPos -= self.__speed
        return (self.__xPos,self.__yPos)
        
    def draw(self):
        #set default draw method creates rectangle
        self.__entityID = self.__canvas.create_rectangle(self.__xPos,self.__yPos,
                                                        self.__xPos+self.__xSize,
                                                        self.__yPos+self.__ySize,
                                                        fill = self.__color)
                                                        
    #specific entity subclass
class Entity(SuperEntity):
    def __init__(self,entityType):
        self.__entityType = entityType
        if entityType == 'B':
            self.__color = (0,255,0)
            self.__xSize = 2
            self.__ySize = 2
            self.__speed = 2
        if entityType == 'C':
            self.__color = (255,255,0)
            self.__xSize = 1
            self.__ySize = 2
            self.__speed = 4
        if entityType == 'D':
            self.__color = (255,255,0)
            self.__xSize = 3
            self.__ySize = 3
            self.__speed = 1
        
    def draw(self):
        if entitType == 'B':
            self.__xSize = 3 #<--delete me, placeholder
        elif entityType == 'C':
            self.__xSize = 3 #<--delete me, placeholder
        elif entityType == 'D':
            self.__xSize = 3 #<--delete me, placeholder
            
            
            
#Begin Application
root = Tk()
root.title("Emergence")
try:
    app = App(root)
    root.mainloop()
except Exception, e:
    print str(e)
    raw_input("Failed to create GUI")
            